package webserver

import (
    "encoding/json"
    "net/http"
    "path/filepath"
    "strings"
    "time"

    "github.com/gin-gonic/gin"
    "go.uber.org/zap"
    "iip/lib"
    "libgo/database/redis"
    "libgo/network/gins/gincors"
    "libgo/network/gins/gincsrf"
    "libgo/network/gins/gingzip"
    "libgo/network/gins/ginjwt"
    "libgo/network/gins/ginsession"
    "libgo/network/gins/ginstatic"
    "libgo/network/gins/ginzap"
    "libgo/utils"
)

type WebServer struct {
    cfg    *Config
    logger *zap.Logger
    r      *gin.Engine

    rds            *redis.RedisDB
    session_store  *ginsession.RediSessiontore
    jwt_middleware *ginjwt.GinJWTMiddleware
}

func New() *WebServer {
    x := &WebServer{}
    return x
}

func (s *WebServer) WithConfig(c *Config) *WebServer {
    s.cfg = c
    return s
}

func (s *WebServer) WithLogger(l *zap.Logger) *WebServer {
    s.logger = l
    return s
}

func (s *WebServer) SessionStore() *ginsession.RediSessiontore {
    return s.session_store
}

func (s *WebServer) GinEngine() *gin.Engine {
    return s.r
}

func (s *WebServer) SetupSession() *WebServer {
    rds := redis.New().WithLogger(s.logger).
        Address(s.cfg.RedisAddr).Password(s.cfg.RedisPass).DB(s.cfg.RedisDB).AssertConnected()
    rdsession := ginsession.NewSessionStore([]byte(s.cfg.SessionHashKey), []byte(s.cfg.SessionBlockKey)).WithRedis(rds.GoRedis())
    s.rds = rds
    s.session_store = rdsession
    return s
}




func (s *WebServer) SetupJWT() *WebServer {
    middle := ginjwt.New()
    middle.Realm = "WebServer"
    middle.SigningAlgorithm = "HS256"
    middle.Key = []byte(s.cfg.JWTKey)
    middle.Timeout = time.Hour
    middle.MaxRefresh = time.Hour










    middle.TokenLookup = "header:Authorization, cookie:jwt, query:token"

    middle.SendCookie = true

    middle.SendAuthorization = false



    if err := middle.Setup(); err != nil {
        s.logger.Error("authMiddleware.MiddlewareInit() Error:" + err.Error())
        return s
    }

    s.jwt_middleware = middle
    return s
}

func (s *WebServer) JWT() *ginjwt.GinJWTMiddleware {
    return s.jwt_middleware
}

func (s *WebServer) JWTProtectedRouterGroup(p string) *gin.RouterGroup {
    g := s.r.Group(p)
    g.Use(s.jwt_middleware.MiddlewareFunc())
    return g
}

func (s *WebServer) SetupCORS() {
    s.r.Use(gincors.New(gincors.Config{
        AllowOrigins:     []string{"*"},
        AllowMethods:     []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
        AllowHeaders:     []string{"Origin", "Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
        ExposeHeaders:    []string{"Content-Length", "Link"},
        AllowCredentials: true,
        AllowOriginFunc: func(origin string) bool {
            return true
        },
        MaxAge: 12 * time.Hour,
    }))
}

func (s *WebServer) SetupCSRF() {
    middle := gincsrf.New([]byte(s.cfg.CSRFKey), func(w http.ResponseWriter, r *http.Request) {
        json_bytes, _ := json.Marshal(gin.H{
            "code":    http.StatusForbidden,
            "message": "csrf auth failed",
        })
        w.Header().Set("Content-Type", "application/json")
        _, _ = w.Write(json_bytes)
    })

    s.r.Use(middle)


    s.r.POST("/csrf/test1", func(c *gin.Context) {
        c.JSON(200, gin.H{"test1": "ok"})
    })
}

func (s *WebServer) SetupNormal() *WebServer {

    isDevMode := func() bool {
        lst := []string{"dev", "develop"}
        for _, ss := range lst {
            if strings.ToLower(ss) == s.cfg.RunMode {
                return true
            }
        }
        return false
    }
    if isDevMode() {
        gin.SetMode(gin.DebugMode)
    } else {
        gin.SetMode(gin.ReleaseMode)
    }

    s.r = gin.New()
    s.r.ForwardedByClientIP = true

    s.r.Use(ginzap.New(s.logger))

    s.r.Use(gin.Recovery())

    s.r.Use(gingzip.New(

        gingzip.DefaultCompression,

        gingzip.WithExcludedExtensions([]string{".pdf", ".mp4"}),

        gingzip.WithExcludedPaths([]string{"/api/"}),
    ))











    s.r.NoRoute(func(c *gin.Context) {
        c.JSON(404, gin.H{
            "code":    404,
            "message": "page not found",
        })
    })


    s.r.GET("/favicon.ico", func(c *gin.Context) {
        c.Data(200, "application/x-ico", lib.DefaultFavicon)
    })
    s.r.Use(ginstatic.Serve("/", ginstatic.LocalFile(filepath.Join(utils.ProjectRoot(), "static"), false)))


    s.r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })

    return s
}

func (s *WebServer) BStart() {
    if err := s.r.Run(s.cfg.Address); err != nil {
        s.logger.Error(err.Error())
    }
}
